using System.Linq.Expressions;
using AdminSG3L.Domain.Entities;
using AdminSG3L.Domain.Repositories;
using AdminSG3L.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace AdminSG3L.Infrastructure.Repositories;

public class EFRepository<T>(AdminDbContext db) : IRepository<T> where T : EntityBase
{
    private readonly AdminDbContext _db = db;

    private readonly DbSet<T> _tb = db.Set<T>();

    /// <summary>
    /// 添加单个实体。
    /// </summary>
    /// <param name="entity">要添加的实体</param>
    public async Task AddAsync(T entity)
    {
        await _tb.AddAsync(entity);
        await _db.SaveChangesAsync();
    }

    /// <summary>
    /// 批量添加实体。
    /// </summary>
    /// <param name="entities">要添加的实体集合</param>
    public async Task AddRangeAsync(IEnumerable<T> entities)
    {
        await _tb.AddRangeAsync(entities);
        await _db.SaveChangesAsync();
    }

    /// <summary>
    /// 获取所有未被软删除的实体总数。
    /// </summary>
    /// <returns>返回所有未被软删除的实体总条数</returns>
    public async Task<int> CountAllAsync()
    {
        return await _tb.CountAsync(e => !e.IsDeleted);
    }

    /// <summary>
    /// 根据条件获取未被软删除的实体数量。
    /// </summary>
    /// <param name="predicate">查询条件表达式</param>
    /// <returns>返回符合条件的实体数量</returns>
    public async Task<int> CountAsync(Expression<Func<T, bool>> predicate)
    {
        return await _tb.Where(e => !e.IsDeleted).CountAsync(predicate);
    }

    /// <summary>
    /// 软删除单个实体（设置IsDeleted为true）。
    /// </summary>
    /// <param name="entity">要软删除的实体</param>
    public async Task DeleteAsync(T entity)
    {
        entity.Delete();
        _tb.Update(entity);
        await _db.SaveChangesAsync();
    }

    /// <summary>
    /// 批量软删除实体（设置IsDeleted为true）。
    /// </summary>
    /// <param name="entities">要软删除的实体集合</param>
    public async Task DeleteRangeAsync(IEnumerable<T> entities)
    {
        foreach (var entity in entities)
        {
            entity.Delete();
        }
        _tb.UpdateRange(entities);
        await _db.SaveChangesAsync();
    }

    /// <summary>
    /// 获取所有未被软删除的实体集合。
    /// </summary>
    /// <returns>返回所有未被软删除的实体集合</returns>
    public async Task<IEnumerable<T>> GetAllAsync()
    {
        return await _tb.Where(e => !e.IsDeleted).ToListAsync();
    }

    /// <summary>
    /// 根据主键获取未被软删除的实体。
    /// </summary>
    /// <param name="id">主键Id</param>
    /// <returns>返回实体对象，查不到时返回null</returns>
    public async Task<T?> GetByIdAsync(Guid id)
    {
        return await _tb.FirstOrDefaultAsync(e => e.Id == id && !e.IsDeleted);
    }

    /// <summary>
    /// 根据条件获取未被软删除的实体集合。
    /// </summary>
    /// <param name="predicate">查询条件表达式</param>
    /// <returns>返回符合条件的实体集合</returns>
    public async Task<IEnumerable<T>> GetManyAsync(Expression<Func<T, bool>> predicate)
    {
        return await _tb.Where(e => !e.IsDeleted).Where(predicate).ToListAsync();
    }

    /// <summary>
    /// 分页查询未被软删除的实体。
    /// </summary>
    /// <param name="predicate">查询条件表达式</param>
    /// <param name="pageIndex">页码（从0开始）</param>
    /// <param name="pageSize">每页数据量</param>
    /// <returns>返回分页后的数据集合和总数</returns>
    public async Task<(IEnumerable<T> Items, int TotalCount)> GetPagedAsync(Expression<Func<T, bool>> predicate, int pageIndex, int pageSize)
    {
        var query = _tb.Where(e => !e.IsDeleted).Where(predicate);
        var totalCount = await query.CountAsync();
        var items = await query.Skip(pageIndex * pageSize).Take(pageSize).ToListAsync();
        return (items, totalCount);
    }

    /// <summary>
    /// 根据条件获取单个未被软删除的实体。
    /// </summary>
    /// <param name="predicate">查询条件表达式</param>
    /// <returns>返回符合条件的单个实体，查不到时返回null</returns>
    public async Task<T?> GetSingleAsync(Expression<Func<T, bool>> predicate)
    {
        return await _tb.Where(e => !e.IsDeleted).FirstOrDefaultAsync(predicate);
    }

    /// <summary>
    /// 更新单个实体。
    /// </summary>
    /// <param name="entity">要更新的实体</param>
    public async Task UpdateAsync(T entity)
    {
        entity.UpdateTime();
        _tb.Update(entity);
        await _db.SaveChangesAsync();
    }
}